package com.ormbench.base;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.servlet.http.HttpServletRequest;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.Message;
import net.sourceforge.stripes.controller.StripesConstants;
import net.sourceforge.stripes.controller.StripesFilter;
import net.sourceforge.stripes.util.CryptoUtil;
import net.sourceforge.stripes.validation.ValidationError;
import net.sourceforge.stripes.validation.ValidationErrors;

public final class StripesUtil {
    public static final String DEFAULT_HEADER = "<div class=\"errorHeader\">Validation Errors</div><ul>";
    public static final String DEFAULT_FOOTER = "</ul>";
    public static final String DEFAULT_BEFORE_ERROR = "<li>";
    public static final String DEFAULT_AFTER_ERROR = "</li>";
    public static final MessageFormat FORMAT_INPUT_HIDDEN = new MessageFormat("<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />");

    private HttpServletRequest request;

    /**
     * Default constructor. Needs to be public for Velocity to treat this as a
     * tool. I believe one will be instantiated for every request that utilizes
     * the tool.
     * 
     */
    public StripesUtil(HttpServletRequest request) {
        super();
        this.request = request;
    }

    /**
     * Errors method replaces the stripes errors taglib
     * 
     * @return
     */
    public String getErrors() {    
        ActionBean actionBean = (ActionBean) request.getAttribute(StripesConstants.REQ_ATTR_ACTION_BEAN);
        ValidationErrors errors = null;
        List<ValidationError> allErrors = new ArrayList<ValidationError>();

        if (actionBean == null || actionBean.getContext().getValidationErrors() == null || actionBean.getContext().getValidationErrors().size() <= 0) {
            return null;
        } else {
            errors = actionBean.getContext().getValidationErrors();
            List<ValidationError> globalErrors = errors.get(ValidationErrors.GLOBAL_ERROR);
            if (globalErrors != null) {
                allErrors.addAll(globalErrors);
            }
            for (List<ValidationError> fieldErrors : errors.values()) {
                if (fieldErrors != null) {
                    allErrors.addAll(fieldErrors);
                }
            }
        }

        // Fetch the header and footer
        Locale locale = this.request.getLocale();
        ResourceBundle bundle = StripesFilter.getConfiguration().getLocalizationBundleFactory().getErrorMessageBundle(locale);
        String header = getResource(bundle, "stripes.errors.header", DEFAULT_HEADER);
        String footer = getResource(bundle, "stripes.errors.footer", DEFAULT_FOOTER);
        String openElement = getResource(bundle, "stripes.errors.beforeError", DEFAULT_BEFORE_ERROR);
        String closeElement = getResource(bundle, "stripes.errors.afterError", DEFAULT_AFTER_ERROR);

        // Sort error messages
		Collections.sort(allErrors, new Comparator<ValidationError>() {
			public int compare(ValidationError ve1, ValidationError ve2) {
				return ve1.getFieldName().compareTo(ve2.getFieldName());
			}
		});

        
        // Write out the error messages
        StringBuffer sb = new StringBuffer();
        if (allErrors.size() > 0) {
        	sb.append(header);
        	for (ValidationError error : allErrors) {
        		sb.append(openElement);
        		sb.append(error.getMessage(locale));
        		sb.append(closeElement);
        	}
        	sb.append(footer);
        }

        return sb.toString();
    }
    
    /**
     * Errors method replaces the stripes messages taglib
     * 
     * @return
     */
    @SuppressWarnings("unchecked")
    public String getMessages() {    
    	StringBuffer sb = new StringBuffer();
    	try {
	    	HttpServletRequest request = this.request;
	        List<Message> messages = (List<Message>) request.getAttribute( StripesConstants.REQ_ATTR_MESSAGES );
	
	        if (messages == null) {
	            messages = (List<Message>) request.getSession().getAttribute( StripesConstants.REQ_ATTR_MESSAGES );
	            request.getSession().removeAttribute( StripesConstants.REQ_ATTR_MESSAGES );
	        }
	        
	        // Fetch the header and footer
	        Locale locale = this.request.getLocale();
	        ResourceBundle bundle = StripesFilter.getConfiguration().getLocalizationBundleFactory().getErrorMessageBundle(locale);
	        String header = getResource(bundle, "stripes.messages.header", DEFAULT_HEADER);
	        String footer = getResource(bundle, "stripes.messages.footer", DEFAULT_FOOTER);
	        String openElement = getResource(bundle, "stripes.messages.beforeMessage", DEFAULT_BEFORE_ERROR);
	        String closeElement = getResource(bundle, "stripes.messages.afterMessage", DEFAULT_AFTER_ERROR);
	
	        // Write out the error messages	        
	        if (messages != null && messages.size() > 0)
	        {
	        	sb.append(header);
	        	for (Message message : messages)
	        	{
	        		sb.append(openElement);
	        		sb.append(message.getMessage(locale));
	        		sb.append(closeElement);
	        	}
	        	sb.append(footer);
	        }
    	} catch (Exception e){
    		//log...
    	}
    	
        return sb.toString();
    }
    
    /**
     * Gets hidden for fields that stripes outputs in form taglib
     * 
     * @return
     */
    public String getHidden() {
        Object[] args = {StripesConstants.URL_KEY_SOURCE_PAGE, CryptoUtil.encrypt(request.getServletPath())};
        return FORMAT_INPUT_HIDDEN.format(args);
    }

    /**
     * Utility method that is used to lookup the resources used for the errors
     * header, footer, and the strings that go before and after each error.
     * 
     * @param bundle
     *            the bundle to look up the resource from
     * @param name
     *            the name of the resource to lookup (prefixes will be added)
     * @param fallback
     *            a value to return if no resource can be found
     * @return the value to use for the named resource
     */
    protected String getResource(ResourceBundle bundle, String name, String fallback) {
        if (bundle == null) {
            return fallback;
        }

        String resource = null;
        try {
            resource = bundle.getString(name);
        } catch (MissingResourceException mre) {
            resource = fallback;
        }

        return resource;
    }
}
